package org.fastiot.protocol.group

import grails.databinding.BindUsing
import org.fastiot.protocol.Packet
import org.fastiot.protocol.codec.BitOutputStream
import org.fastiot.protocol.codec.BitSource

/**
 * 序列编组
 */
class Sequence implements Groupable {

    /**
     * （应用）范围
     */
    @BindUsing({ obj, source ->
        Packet.Part.byValue(Integer.parseInt(source["part"] as String))
    })
    Packet.Part part

    /**
     * 隶属（结构/编组）/父类型
     */
    @BindUsing({ obj, source ->
        Affiliation.byValue(Integer.parseInt(source["parentType"] as String))
    })
    Affiliation parentType

    /**
     * 隶属（结构/编组）代码
     */
    Integer parentCode
    /**
     * 隶属（结构/编组）代码（Hex）
     */
    String hexParentCode

    /**
     * 排列序号
     */
    Integer serialNo
    /**
     * 数据标识符
     */
    String dataIdentifier
    /**
     * （编组）名称
     */
    String name
    /**
     * （基础）规则代码
     */
    String ruleCode

    boolean enabled = false

    String remark
    Date dateCreated
    Date lastUpdated

    static transients = ['hexParentCode']

    static constraints = {
        part(nullable: false, inList: Packet.Part.values().toList())
        parentType(nullable: true, inList: Affiliation.values().toList(), validator: Sequence.ValidatePartRules)
        parentCode(nullable: true, min: 0, validator: Sequence.ValidatePartRules)
        hexParentCode(blank: true, matches: /[a-fA-F0-9]+/)
        serialNo(nullable: false, min: 1)
        dataIdentifier(blank: false)
        name(blank: false)
        ruleCode(blank: true, unique: [ "part", "parentType", "parentCode" ], matches: /[a-zA-Z0-9]+/)
        enabled(blank: false)
        remark(nullable: true, widget: 'textarea')
    }

    static ValidatePartRules = { val, obj, errors ->
        if (obj.part == Packet.Part.Body && val == null) {
            errors.rejectValue("part", "Properties of parent must be set when 'Part.Body' is selected")
        } else if (obj.part != Packet.Part.Body && val != null) {
            errors.rejectValue("part", "Properties of parent must be NULL when 'Part.Header' or 'Part.Checking' is selected")
        }
    }

    static mapping = {
        remark type: 'text'
        sort serialNo: "asc"
    }

    def beforeValidate() {
        if (hexParentCode && !parentCode)
            parentCode = Integer.parseInt(hexParentCode, 16)
    }

    def afterLoad() {
        if (parentCode)
            hexParentCode = Integer.toHexString(parentCode)
    }

    @Override
    void decode(final BitSource bitSource, final Map contents) {
        throw new UnsupportedOperationException("The method is not implemented")
    }

    @Override
    void encode(final BitOutputStream bitOutput, final Map contents) {
        throw new UnsupportedOperationException("The method is not implemented")
    }
}
